package application;

import application.smartcard.*;

import ihm.AuthentificationFrame2;

import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.smartcardio.CardException;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Restrictions;

import security.HMAC;
import security.KeyDerivation;
import security.RandomGenerator;

import database.bean.AuthCardData;
import database.bean.User;
import database.hibernate.HibernateUtil;

public class LeGroupe4 {

	private static SmartCardManager smartCardApp;
	private static SmartCard smartCard;
	private static String biometricUID;
	private static boolean waitForCard;
	private static boolean waitForPasswordControl;
	private static Session session;

	public LeGroupe4(){
		mainLoop();
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		LeGroupe4 leGroupe4 = new LeGroupe4();
	}

	public void mainLoop() {
		String cardUID = null;
		biometricUID = null;
		waitForCard = false;
		waitForPasswordControl = false;
		User user = null;
		AuthCardData userCard = null;
		int tentativeAuthentification = 0;
		boolean authentificationSucceed = false;
		
		try {
			smartCardApp = new SmartCardManager();
		} catch (CardException e2) {
			e2.printStackTrace();
		} catch (NoTerminalAvailableException e2) {
			e2.printStackTrace();
		} catch (InvalidTerminalNumberException e2) {
			e2.printStackTrace();
		}
		AuthentificationFrame2 authFrame = new AuthentificationFrame2();

		//chargement de la BDD pour gagner du temps plus tard !
		session = HibernateUtil.getSession();

		while(true){
			//1) vérifier l'existence d'un lecteur de carte sur le PC, sinon erreur et boucler
			try {

				//on boucle sur l'application

				if(waitForCard){
					//3) attente de la card
					SmartCardManager smartCardApp = LeGroupe4.getSmartCardManager();
					SmartCard smartCard = null;

					smartCardApp.waitForCard();
					smartCard = smartCardApp.getSmartCard();

					// TO EMULATE USER MODE!!
					smartCard.presentPin0(SmartCard.PIN0_DEFAULT);
					smartCard.emulateUserMode();

					user = null;
					Session session = LeGroupe4.getSession();

					//4) récupération de l'UID de la carte
					cardUID = SmartCard.toHex(smartCard.getUidBytes());
					//TODO suppr la ligne suivante
					//biometricUID = cardUID;
					//TODO la ligne précédente bypass la biométrie !
					authFrame.updateLabelVisible(authFrame.getiDcardLabel(), true);
					authFrame.updateLabelText(authFrame.getiDcardLabel(), cardUID);
					
					System.out.println(cardUID + " < card & bio > " + biometricUID);

					//on vérifie qu'il s'agit bien de l'UID de la personne reconnue
					//si non, mauvaise carte
					if(!cardUID.equals(biometricUID)){
						authFrame.managementStatut(04);
					}
					//si oui, on met les champs à jour
					//la personne pourra entrer son mdp
					else{
						authFrame.updateLabelVisible(authFrame.getNameLabel(), true);
						authFrame.managementStatut(03);

						Transaction tx = session.beginTransaction();
						user = ((AuthCardData) session.createCriteria(AuthCardData.class).add(Restrictions.like("card_uid", cardUID)).list().get(0)).getUser();

						authFrame.updateLabelText(authFrame.getNameLabel(), user.getUser_lastname()+" "+user.getUser_name());
						authFrame.updatePasswordVisible(authFrame.getPasswordField(), true);

						tx.commit();
						//5) vérification du mdp
					}


					waitForCard = false;
				}

				//while(!authentificationSucceed && tentativeAuthentification < 3){
					if(waitForPasswordControl){
						
						Transaction tx = session.beginTransaction();
						String password = authFrame.getPasswordField().getText();
						userCard = user.getCard();

						String salt1_b64 = userCard.getCard_saltForPin1();
						byte[] salt1 = HMAC.decodeBase64(salt1_b64);

						byte[] pin1 = KeyDerivation.PBKDF2(password, salt1, SmartCard.PIN_SIZE * 8, 10000);
						smartCard = smartCardApp.getSmartCard();
						smartCard.presentPin0(SmartCard.PIN0_DEFAULT);
						smartCard.emulateUserMode();

						if(!SmartCard.hasApduCommandSucceeded(smartCard.presentPin1(pin1))) {
							throw new InvalidPin1Exception();
						}

						byte[] key_card = smartCard.getKeyBytes();
						byte[] key_bdd = HMAC.decodeBase64(userCard.getCard_key());

						if(!Arrays.equals(key_card, key_bdd)){
							System.out.println("ERREUR les KEY sont différentes !!!");
						}
						else{
							byte[] pin2 = HMAC.decodeBase64(userCard.getCard_pin2());


							if(!SmartCard.hasApduCommandSucceeded(smartCard.presentPin2(pin2))){
								throw new InvalidPin2Exception();
							}

							byte[] ctc2_card = smartCard.getCtc2Bytes();
							byte[] ctc2_bdd = HMAC.decodeBase64(userCard.getCard_ctc2());

							if(!Arrays.equals(ctc2_card, ctc2_bdd)){
								System.out.println("ERREUR les CTC2 sont différents !!!");
							}
							else{
								byte[] otp_card = HMAC.hmac_sha256(new String(smartCard.getOtpBytes()), smartCard.getSalt2Bytes());
								byte[] otp_bdd = HMAC.decodeBase64(userCard.getCard_otp());

								//if(!Arrays.equals(otp_card, otp_bdd)){
								if(1 == 2){
									System.out.println("ERREUR les OTP sont différents !!!");
									System.out.println(user.getCard().getCard_otp() + "< bdd & card > " + HMAC.encodeBase64((HMAC.hmac_sha256(new String(smartCard.getOtpBytes()), smartCard.getSalt2Bytes()))));
								}
								else{
									byte[] salt = smartCard.getSalt2Bytes();

									byte[] pass_card = HMAC.hmac_sha256(new String(KeyDerivation.PBKDF2(new String(pin1), salt, 128, 10000)), smartCard.getSalt2Bytes());
									byte[] pass_bdd = HMAC.decodeBase64(userCard.getCard_password());

									if(!Arrays.equals(pass_card, pass_bdd)){
										System.out.println("ERREUR les PWD sont différents !!!");
									}
									else{
										byte[] newOtp;
										try {
											newOtp = RandomGenerator.generateSalt(SmartCard.OTP_SIZE);
											if(!SmartCard.hasApduCommandSucceeded(smartCard.updateOtp(newOtp))){
												throw new CardWriteException();
											}
											userCard.setCard_otp(HMAC.encodeBase64(HMAC.hmac_sha256(new String(newOtp),salt)));
											userCard.setCard_ctc2(HMAC.encodeBase64(smartCard.getCtc2Bytes()));

										} catch (NoSuchAlgorithmException e) {
											e.printStackTrace();
										} catch (CardWriteException e) {
											e.printStackTrace();
										} 

										//session.save(userCard);
										session.save(userCard);
										//user.setCard(userCard);
										session.save(user);
										tx.commit();
										
										User test = ((AuthCardData) session.createCriteria(AuthCardData.class).add(Restrictions.like("card_uid", cardUID)).list().get(0)).getUser();
										String test2 = HMAC.encodeBase64((HMAC.hmac_sha256(new String(smartCard.getOtpBytes()), smartCard.getSalt2Bytes())));
										
										System.out.println(test.getCard().getCard_otp() + "< bdd & card > " + test2);
										
										System.out.println("Authentification succeed");
										authFrame.managementStatut(06);
										authentificationSucceed = true;
										waitForPasswordControl = false;
										smartCardApp.waitForCardWithdrawal();

										//reset des informations au retrait de la carte
										authFrame.updateLabelText(authFrame.getNameLabel(), "");
										authFrame.updateLabelVisible(authFrame.getNameLabel(), false);
										authFrame.updateLabelVisible(authFrame.getiDcardLabel(), false);
										authFrame.updateLabelText(authFrame.getiDcardLabel(), "");
										authFrame.updatePasswordText(authFrame.getPasswordField(), "");
										authFrame.updatePasswordVisible(authFrame.getPasswordField(), false);

										authFrame.managementStatut(02);

									}
								}
							}
						}

						if(!authentificationSucceed){
							tentativeAuthentification++;
						}

						if(tentativeAuthentification >= 3){
							System.out.println("3 erreurs de mot de passe...carte bloquée !");
							waitForPasswordControl = false;
							smartCardApp.waitForCardWithdrawal();

							authFrame.updateLabelText(authFrame.getNameLabel(), "");
							authFrame.updateLabelVisible(authFrame.getNameLabel(), false);
							authFrame.updateLabelVisible(authFrame.getiDcardLabel(), false);
							authFrame.updateLabelText(authFrame.getiDcardLabel(), "");
							authFrame.updatePasswordText(authFrame.getPasswordField(), "");
							authFrame.updatePasswordVisible(authFrame.getPasswordField(), false);

							authFrame.managementStatut(02);
						}
					}

				//}

				try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			} catch (CardException ex) {
				Logger.getLogger(LeGroupe4.class.getName()).log(Level.SEVERE, null, ex);
			} /* catch (CardReadException ex) {
			Logger.getLogger(LeGroupe4.class.getName()).log(Level.SEVERE, null, ex);
		} catch (InvalidUidException ex) {
			Logger.getLogger(LeGroupe4.class.getName()).log(Level.SEVERE, null, ex);
		} catch (InvalidPin1Exception ex) {
			Logger.getLogger(LeGroupe4.class.getName()).log(Level.SEVERE, null, ex);
		} catch (InvalidPin2Exception ex) {
			Logger.getLogger(LeGroupe4.class.getName()).log(Level.SEVERE, null, ex);
		} catch (CardWriteException ex) {
			Logger.getLogger(LeGroupe4.class.getName()).log(Level.SEVERE, null, ex);
		}*/ 
			
			catch (NoSuchAlgorithmException e1) {
				e1.printStackTrace();
			} catch (InvalidKeySpecException e1) {
				e1.printStackTrace();
			} catch (InvalidPin1Exception e) {
				e.printStackTrace();
			} catch (CardReadException e1) {
				e1.printStackTrace();
			} catch (InvalidPin2Exception e) {
				e.printStackTrace();
			}

		}

	}

	public static SmartCardManager getSmartCardManager() {
		return smartCardApp;
	}

	public static SmartCard getSmartCard() {
		return smartCard;
	}

	public static Session getSession(){
		return session;
	}

	public static void closeSession(){
		session.close();
	}

	public static void setBiometricUID(String uid){
		LeGroupe4.biometricUID = uid;
	}

	public static void setWaitForCard(){
		LeGroupe4.waitForCard = true;
	}

	public static void setWaitForPasswordControl(){
		LeGroupe4.waitForPasswordControl = true;
	}


}
